Micron Document
πŸŽ–οΈGitΠ―Ρ€Π°πŸŽ–οΈ


Displaying Rendered β€’ View raw β€’ Download

core/takserver/README.md 2d20cd8a4708e2ef66e98d5259f3a97ec93f240a (2d20cd8a) Text, 9.80 KB

T383838:core:takserver

Overview

The T383838:core:takserver module implements the Meshtastic ↔ TAK (Team Awareness Kit) bridge. It embeds an mTLS TCP server (port 8089) compatible with ATAK (Android), iTAK (iOS), and WinTAK clients, enabling mesh-networked position sharing and GeoChat with TAK-enabled devices.

Targets: Android Β· JVM (Desktop) Β· iOS β€” fully multiplatform with T383838expect/T383838actual splits for compression, file I/O, and the TCP server itself.

Key Responsibilities

β€’ Serve an mTLS TCP listener (port 8089) compatible with the CoT (Cursor-on-Target) protocol
β€’ Convert Meshtastic protobuf packets (T383838TAKPacketV2) to CoT XML events and vice versa
β€’ Generate ATAK Data Package T383838.zip exports (team contacts, map overlays)
β€’ Compress CoT payloads using Zstd (TAK SDK format) with T383838expect/T383838actual platform implementations
β€’ Buffer up to 50 CoT messages for 5 minutes when no TAK clients are connected; drain on reconnect
β€’ Provide Crowdin-localised TAK preference XML for ATAK client provisioning

Source Structure

T282828
src/
β”œβ”€β”€ commonMain/kotlin/org/meshtastic/core/takserver/
β”‚ β”œβ”€β”€ TAKServer.kt ← interface + expect createTAKServer()
β”‚ β”œβ”€β”€ TAKServerManager.kt ← interface + TAKServerManagerImpl (offline queue)
β”‚ β”œβ”€β”€ TAKMeshIntegration.kt ← bridges mesh service ↔ TAK server
β”‚ β”œβ”€β”€ CoTConversion.kt ← Position/User β†’ CoTMessage extension fns
β”‚ β”œβ”€β”€ CoTXml.kt / CoTXmlParser.kt / CoTXmlFrameBuffer.kt
β”‚ β”œβ”€β”€ CoTXmlDataClasses.kt
β”‚ β”œβ”€β”€ CoTDetailStripper.kt
β”‚ β”œβ”€β”€ TAKModels.kt ← CoTMessage, TAKClientInfo, TAKConnectionEvent
β”‚ β”œβ”€β”€ TAKPacketConversion.kt
β”‚ β”œβ”€β”€ TAKPacketV2Conversion.kt
β”‚ β”œβ”€β”€ TAKDefaults.kt
β”‚ β”œβ”€β”€ TAKDataPackageGenerator.kt
β”‚ β”œβ”€β”€ RouteDataPackageGenerator.kt
β”‚ β”œβ”€β”€ TAKPrefXmlDataClasses.kt
β”‚ β”œβ”€β”€ TakV2TypeMapper.kt
β”‚ β”œβ”€β”€ TakConversionHelpers.kt
β”‚ β”œβ”€β”€ XmlUtils.kt
β”‚ β”œβ”€β”€ AtakFileWriter.kt ← expect
β”‚ β”œβ”€β”€ TakSdkCompressor.kt ← expect (Zstd TAK-SDK frame)
β”‚ β”œβ”€β”€ TakV2Compressor.kt ← expect (Zstd TAKPacketV2 frame)
β”‚ β”œβ”€β”€ ZipArchiver.kt ← expect
β”‚ β”œβ”€β”€ TakFixtureLoader.kt ← expect (test fixtures)
β”‚ β”œβ”€β”€ TakMeshTestRunner.kt
β”‚ └── di/
β”‚ └── CoreTakServerModule.kt
β”œβ”€β”€ jvmAndroidMain/kotlin/ ← actual TAKServerJvm, TAKClientConnection, TakCertLoader
β”œβ”€β”€ androidMain/kotlin/ ← actual AtakFileWriter (Android)
β”œβ”€β”€ jvmMain/kotlin/ ← actual AtakFileWriter (Desktop), XML pull-parser
└── iosMain/kotlin/ ← actual TAKServerIos, actual compression impls

Notable APIs

T383838TAKServer (interface)

T282828
Tff7b72interface T56d364TAKServer Tb4b4b4{
Tff7b72val Te6edf3connectionCountTb4b4b4: Te6edf3StateFlowTff7b72<Tffa657IntTff7b72>
Tff7b72var Te6edf3onMessageTb4b4b4: Tb4b4b4(Tb4b4b4(Te6edf3CoTMessageTb4b4b4, Te6edf3TAKClientInfo?Tb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4)Tff7b72?
Tff7b72var Te6edf3onClientConnectedTb4b4b4: Tb4b4b4(Tb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4)Tff7b72?

Tff7b72suspend Tff7b72fun Td2a8ffstartTb4b4b4(Te6edf3scopeTb4b4b4: Te6edf3CoroutineScopeTb4b4b4)Tb4b4b4: Te6edf3ResultTff7b72<Tffa657UnitTff7b72>
Tff7b72fun Td2a8ffstopTb4b4b4(Tb4b4b4)
Tff7b72suspend Tff7b72fun Td2a8ffbroadcastTb4b4b4(Te6edf3cotMessageTb4b4b4: Te6edf3CoTMessageTb4b4b4)
Tff7b72suspend Tff7b72fun Td2a8ffbroadcastRawXmlTb4b4b4(Te6edf3xmlTb4b4b4: Tffa657StringTb4b4b4)
Tff7b72suspend Tff7b72fun Td2a8ffhasConnectionsTb4b4b4(Tb4b4b4)Tb4b4b4: Tffa657Boolean
Tb4b4b4}


The mTLS listener binds on port 8089 using a bundled T383838server.p12 / T383838ca.pem identity, compatible with the ATAK Data Package provisioning flow.

T383838TAKServerManager (interface)

T282828
Tff7b72interface T56d364TAKServerManager Tb4b4b4{
Tff7b72val Te6edf3isRunningTb4b4b4: Te6edf3StateFlowTff7b72<Tffa657BooleanTff7b72>
Tff7b72val Te6edf3connectionCountTb4b4b4: Te6edf3StateFlowTff7b72<Tffa657IntTff7b72>
Tff7b72val Te6edf3inboundMessagesTb4b4b4: Te6edf3SharedFlowTff7b72<Te6edf3InboundCoTMessageTff7b72>

Tff7b72suspend Tff7b72fun Td2a8ffstartTb4b4b4(Te6edf3scopeTb4b4b4: Te6edf3CoroutineScopeTb4b4b4)
Tff7b72fun Td2a8ffstopTb4b4b4(Tb4b4b4)
Tff7b72suspend Tff7b72fun Td2a8ffbroadcastTb4b4b4(Te6edf3cotMessageTb4b4b4: Te6edf3CoTMessageTb4b4b4)
Tff7b72suspend Tff7b72fun Td2a8ffbroadcastRawXmlTb4b4b4(Te6edf3xmlTb4b4b4: Tffa657StringTb4b4b4)
Tb4b4b4}


T383838TAKServerManagerImpl adds an offline queue: buffers up to 50 CoT messages for 5 minutes when no clients are connected and drains them automatically on the next T383838onClientConnected callback.

T383838CoTMessage

T282828
Tf0883e@Serializable
Tff7b72data Tff7b72class T56d364CoTMessageTb4b4b4(
Tff7b72val Te6edf3uidTb4b4b4: Tffa657StringTb4b4b4,
Tff7b72val Te6edf3typeTb4b4b4: Tffa657StringTb4b4b4, T8b949e// e.g. "a-f-G-U-C" (friendly ground unit)
Tff7b72val Te6edf3timeTb4b4b4: Te6edf3InstantTb4b4b4,
Tff7b72val Te6edf3latTb4b4b4: Tffa657DoubleTb4b4b4, Tff7b72val Te6edf3lonTb4b4b4: Tffa657DoubleTb4b4b4, Tff7b72val Te6edf3haeTb4b4b4: Tffa657DoubleTb4b4b4,
Tff7b72val Te6edf3contactTb4b4b4: Te6edf3CoTContact?Tb4b4b4,
Tff7b72val Te6edf3groupTb4b4b4: Te6edf3CoTGroup?Tb4b4b4,
Tff7b72val Te6edf3trackTb4b4b4: Te6edf3CoTTrack?Tb4b4b4,
Tff7b72val Te6edf3chatTb4b4b4: Te6edf3CoTChat?Tb4b4b4,
Tff7b72val Te6edf3remarksTb4b4b4: Tffa657String?Tb4b4b4,
T8b949e// ...
Tb4b4b4)

T8b949e// Factory helpers
Te6edf3CoTMessageTb4b4b4.Te6edf3pliTb4b4b4(Te6edf3uidTb4b4b4, Te6edf3callsignTb4b4b4, Te6edf3latTb4b4b4, Te6edf3lonTb4b4b4, Tb4b4b4.Tb4b4b4.Tb4b4b4.Tb4b4b4) T8b949e// Position Location Information
Te6edf3CoTMessageTb4b4b4.Te6edf3chatTb4b4b4(Te6edf3senderUidTb4b4b4, Te6edf3callsignTb4b4b4, Te6edf3messageTb4b4b4, Te6edf3chatroomTb4b4b4)


CoT Conversion

T282828
T8b949e// Meshtastic proto β†’ CoT
Te6edf3orgTb4b4b4.Te6edf3meshtasticTb4b4b4.Te6edf3protoTb4b4b4.Te6edf3PositionTb4b4b4.Te6edf3toCoTMessageTb4b4b4(Te6edf3uidTb4b4b4, Te6edf3callsignTb4b4b4, Te6edf3teamTb4b4b4, Te6edf3roleTb4b4b4, Te6edf3batteryTb4b4b4)Tb4b4b4: Te6edf3CoTMessage
Te6edf3orgTb4b4b4.Te6edf3meshtasticTb4b4b4.Te6edf3protoTb4b4b4.Te6edf3UserTb4b4b4.Te6edf3toCoTMessageTb4b4b4(Te6edf3positionTb4b4b4, Te6edf3teamTb4b4b4, Te6edf3roleTb4b4b4, Te6edf3batteryTb4b4b4)Tb4b4b4: Te6edf3CoTMessage


Dependency Graph

T282828
core:takserver
β”œβ”€β”€ api β†’ core:repository (exported)
β”œβ”€β”€ core:common, core:di, core:model, org.meshtastic:protobufs (Maven)
β”œβ”€β”€ okio, kotlinx.serialization.json
β”œβ”€β”€ xmlutil-core, xmlutil-serialization
β”œβ”€β”€ ktor-client-core, ktor-network (TCP socket)
└── kotlinx.datetime, kermit (zstd rides on the SDK's transitive kzstd)

Local TAK Server Feature

The Local TAK Server can be enabled from the app's Settings screen. When running, ATAK/iTAK clients on the same network can connect to T383838<device-ip>:8089 and their position reports are automatically bridged onto the mesh. Mesh node positions are broadcast to all connected TAK clients in real time.

TAKPacket-SDK consumer & version-bump playbook

This module consumes the external TAKPacket-SDK (T383838org.meshtastic:takpacket-sdk, KMP since 0.7.0; pinned as T383838takpacket-sdk in T383838gradle/libs.versions.toml, currently 0.8.0) for the V2 wire format. The SDK does CoT-XML ↔ T383838TAKPacketV2 ↔ zstd-compressed bytes; it owns the dictionaries and the schema. The T383838TAKPacketV2 proto types themselves come from the T383838org.meshtastic:protobufs Maven artifact (pinned as T383838meshtastic-protobufs, api()-exported by T383838:core:model).

Two V2 wire paths β€” keep both in mind when the SDK changes:

β€’ Path A (primary, SDK-delegated): T383838TakSdkCompressor / T383838TakV2Compressor call the SDK's parser/builder/compressor. This path is insulated from proto field renames as long as the SDK and T383838meshtastic-protobufs versions are bumped together.
β€’ Path B (fallback): T383838TAKPacketV2Conversion.kt builds and reads the Wire-generated T383838TAKPacketV2 directly (SDK-failure send fallback; iOS receive stub). It references proto fields by name, so it breaks at compile time on any schema change and must be updated in lockstep.

When bumping to a new (wire-breaking) SDK version:
1. T383838gradle/libs.versions.toml β†’ bump T383838takpacket-sdk (and, if the schema moved, T383838meshtastic-protobufs to the matching protobufs release).
2. Leave T383838:core:model's exclude block intact (T383838core/model/build.gradle.kts): the SDK still declares a transitive, older T383838org.meshtastic:protobufs pin, so T383838:core:model api()-exports the SDK with T383838exclude(group = "org.meshtastic", module = "protobufs" / "protobufs-jvm" / "protobufs-android") β€” that keeps the app's single protobufs version authoritative and prevents duplicate-class / proto-ABI breakage. (The T383838.toString() string-notation there is load-bearing: catalog dependencies are immutable, so T383838exclude {} only works on the string copy.)
3. Update Path B (T383838TAKPacketV2Conversion.kt) and the bridge (T383838TakV2Compressor.kt) for any renamed/removed/added wire fields.
4. Test: T383838./gradlew :core:takserver:allTests :core:takserver:compileKotlinJvm (full KMP validation; T383838:core:takserver:jvmTest works as a faster focused check) β€” against a locally published SDK add T383838-PuseMavenLocal (gated in T383838settings.gradle.kts); against a published version add T383838--refresh-dependencies instead.

Wire facts (don't re-introduce phantom changes): PLI is implicit β€” no payload variant + an T383838a-f-* cot type is a PLI. T383838DrawnShape vertices are two packed T383838repeated sint32 delta columns. T383838course stays T383838degΓ—100, T383838uid stays a string, T383838stale_seconds stays tag 16 β€” deliberate; do not "fix" them in T383838TAKPacketV2Conversion.kt.

Debug "Send Test CoTs": T383838TakMeshTestRunner sends the bundled T383838tak_test_fixtures/*.xml through the SDK path (parse β†’ strip β†’ compress β†’ send). They ride the SDK path, so they need no edits across wire breaks β€” they ARE the regression surface.

Dependency Graph

<!--region graph-->
T282828
Te6edf3graph Te6edf3TB
:Te6edf3coreTb4b4b4:Te6edf3takserverTff7b72[Te6edf3takserverTff7b72]Tff7b72:::Te6edf3kmpTff7b72-Te6edf3library
:Te6edf3coreTb4b4b4:Te6edf3takserver T8b949e--> :core:repository
:Te6edf3coreTb4b4b4:Te6edf3takserver Tff7b72-Tb4b4b4.Tff7b72-Tff7b72> :Te6edf3coreTb4b4b4:Te6edf3common
:Te6edf3coreTb4b4b4:Te6edf3takserver Tff7b72-Tb4b4b4.Tff7b72-Tff7b72> :Te6edf3coreTb4b4b4:Te6edf3di
:Te6edf3coreTb4b4b4:Te6edf3takserver Tff7b72-Tb4b4b4.Tff7b72-Tff7b72> :Te6edf3coreTb4b4b4:Te6edf3model
:Te6edf3coreTb4b4b4:Te6edf3takserver Tff7b72-Tb4b4b4.Tff7b72-Tff7b72> :Te6edf3coreTb4b4b4:Te6edf3testing

Te6edf3classDef Te6edf3androidTff7b72-Te6edf3application Te6edf3fillTb4b4b4:Te6edf3#CAFFBFTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3androidTff7b72-Te6edf3applicationTff7b72-Te6edf3compose Te6edf3fillTb4b4b4:Te6edf3#CAFFBFTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3composeTff7b72-Te6edf3desktopTff7b72-Te6edf3application Te6edf3fillTb4b4b4:Te6edf3#CAFFBFTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3androidTff7b72-Te6edf3feature Te6edf3fillTb4b4b4:Te6edf3#FFD6A5Tb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3androidTff7b72-Te6edf3library Te6edf3fillTb4b4b4:Te6edf3#9BF6FFTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3androidTff7b72-Te6edf3libraryTff7b72-Te6edf3compose Te6edf3fillTb4b4b4:Te6edf3#9BF6FFTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3androidTff7b72-Te6edf3test Te6edf3fillTb4b4b4:Te6edf3#A0C4FFTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3jvmTff7b72-Te6edf3library Te6edf3fillTb4b4b4:Te6edf3#BDB2FFTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3kmpTff7b72-Te6edf3feature Te6edf3fillTb4b4b4:Te6edf3#FFD6A5Tb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3kmpTff7b72-Te6edf3libraryTff7b72-Te6edf3compose Te6edf3fillTb4b4b4:Te6edf3#FFC1CCTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3kmpTff7b72-Te6edf3library Te6edf3fillTb4b4b4:Te6edf3#FFC1CCTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Tff7b72unknown Te6edf3fillTb4b4b4:Te6edf3#FFADADTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;


<!--endregion-->

Served by rngit 1.5.2 - Generated in 0.12s